/**
 * Copyright (c) 2007, Blue Hole Software. All rights reserved.
 * Code licensed under the Apache 2.0 License:
 * http://www.apache.org/licenses/
 */

package org.bhf.providers.security.authorization.dbpolicy;

import com.google.inject.Inject;
import org.bhf.db.txn.Transaction;
import org.bhf.db.txn.TransactionModule;
import org.bhf.providers.security.authorization.PolicyProvider;
import org.bhf.security.authorization.ParameterizablePermission;
import org.bhf.security.authorization.Permissions;
import org.bhf.security.common.Role;

import java.lang.reflect.Constructor;
import java.security.Permission;
import java.security.PermissionCollection;
import java.security.Principal;
import java.util.List;
import java.util.logging.Level;

/**
 * <p>
 * <code>PolicyManager</code> based on a database schema.
 */
public class DBPolicyProvider implements PolicyProvider
{
    private static final Class[] PERMISSION_CTOR_ONE_ARG    = new Class[] { String.class };
    private static final Class[] PERMISSION_CTOR_TWO_ARGS   = new Class[] { String.class, String.class };

    @Inject
    private TransactionModule.Provider  transaction;

    /**
     * @inheritDoc
     */
    public void                 refresh()
    {
        // We go directly to the database, no local caching, so refresh() is a noop
        // Consumers, such as the RoleBaseSecurityManager, may do caching.
    }

    /**
     * @inheritDoc
     */
    @Transaction( unit = "authorization" )
    public PermissionCollection getPermissions( final Permission aPermission, final Principal principal )
    {
        // We can't throw exceptions from here. Any partially loaded policy would be dangerous to use
        // because it might be missing denies as well as grants. Thus, we log the problem and return
        // an empty collection on error.
        try
        {
            final List<PermissionEntity> ps = PermissionEntity.byPrincipalAndPermission( transaction.getEntityManager(), principal, aPermission );
            final PermissionCollection permissions = new Permissions();

            for( final PermissionEntity p : ps )
            {
                final Class permissionClass = Class.forName( p.getType(), false, Thread.currentThread().getContextClassLoader() );

                Permission permission;
                if( p.getActions() == null || p.getActions().length() == 0 )
                {
                    final Constructor<?> ctor = permissionClass.getConstructor( PERMISSION_CTOR_ONE_ARG );
                    permission = (Permission)ctor.newInstance( p.getName() );
                }
                else
                {
                    final Constructor ctor = permissionClass.getConstructor( PERMISSION_CTOR_TWO_ARGS );
                    permission = (Permission)ctor.newInstance( p.getName(), p.getActions() );
                }

                // Is this a parameterizable permission with a matching role?
                if( (permission instanceof ParameterizablePermission) && (principal instanceof Role) )
                {
                    final Role                      role    = (Role)principal;
                    final ParameterizablePermission pp      = (ParameterizablePermission)permission;
                    final Permission[]              ppz     = pp.parameterize( role.getParameters() );

                    for( Permission aPpz : ppz ) permissions.add( aPpz );
                }
                else
                {
                    permissions.add( permission );
                }
            }

            return permissions;
        }
        catch( Exception e )
        {
            Package.log.log( Level.SEVERE, e.getMessage(), e );
            return new Permissions();
        }
    }
}



